#include <QFile>
#include <QDataStream>
#include <QMutableHashIterator>
#include <com_deepin_dde_notification.h>
#include "cscheduledbus.h"
#include "CalendarDbusUI.h"
#include "DataCenter.h"

void notifyUser(QString content)
{
    QString title = QObject::tr("Calendar Widget");
    QString body = content; //QObject::tr("show on the desktop");
    QStringList actionlist;
    QString appicon("dde-calendar");
    qint32 timeout = 3000;
    QVariantMap hits;

    __Notification m_dbusnotify("com.deepin.dde.Notification",
                            "/com/deepin/dde/Notification",
                            QDBusConnection::sessionBus(),
                            nullptr);

    m_dbusnotify.Notify(qAppName(), 0, appicon, title, body, actionlist, hits, timeout);
}

void appendUncheckedTasks(QVector<ScheduleDataInfo> &schedules, QSet<int> &ids)
{
    QMap<QDate, QVector<ScheduleDataInfo> > info;
    CScheduleDBus::getInstance()->GetJobs(QDate::currentDate().addDays(-3), QDate::currentDate(), info);
    QMapIterator<QDate, QVector<ScheduleDataInfo>> i(info);
    while (i.hasNext())
    {
        i.next();
        for (const ScheduleDataInfo& item : i.value())
        {
            if (!ids.contains(item.getID()) && !DataCenter::instance().getExtendedData(item).isChecked) // and is unchecked
            {
                ids.insert(item.getID());
                schedules.append(item);
            }
        }

    }
}

void DataCenter::getJobs(DisplayInfo displayInfo, QVector<ScheduleDataInfo> &schedules)
{
    schedules.clear();
    QMap<QDate, QVector<ScheduleDataInfo> > info;
    QSet<int> ids;
    if (displayInfo == Passed)
    {
        CScheduleDBus::getInstance()->GetJobs(QDate::currentDate().addDays(-3), QDate::currentDate(), info);
        QMapIterator<QDate, QVector<ScheduleDataInfo>> i(info);
        while (i.hasNext())
        {
            i.next();
            for (const ScheduleDataInfo& item : i.value())
            {
                if (!ids.contains(item.getID()))
                {
                    ids.insert(item.getID());
                    schedules.append(item);
                }
            }

        }
    }
    if (displayInfo == Home)
    {
        // find recent unchecked tasks
        appendUncheckedTasks(schedules, ids);
        CScheduleDBus::getInstance()->GetJobs(QDate::currentDate(), QDate::currentDate().addDays(6), info);
        QMapIterator<QDate, QVector<ScheduleDataInfo>> i(info);
        bool occupied = false;
        while (i.hasNext())
        {
            i.next();
            // find occupied tasks
            if (!occupied && !i.value().isEmpty())
            {
                occupied = true;
                for (const ScheduleDataInfo& item : i.value())
                {
                    if (!ids.contains(item.getID()))
                    {
                        ids.insert(item.getID());
                        schedules.append(item);
                    }
                }
            }
            // find notify tasks
            for (const ScheduleDataInfo& item : i.value())
            {
                if (!ids.contains(item.getID()) &&
                        (item.getRemindData().getRemindNum() != -1 ||
                        DataCenter::getExtendedData(item).piningHint ||
                        DataCenter::getExtendedData(item).countdownHint))

                {
                    ids.insert(item.getID());
                    schedules.append(item);
                }
            }
        }
    }
    int day = int(displayInfo);
    if (day > 0)
    {
        CScheduleDBus::getInstance()->GetJobs(QDate::currentDate().addDays(day), QDate::currentDate().addDays(day), info);
        for (const ScheduleDataInfo& item : info.first())
        {
            if (!ids.contains(item.getID()))
            {
                ids.insert(item.getID());
                schedules.append(item);
            }
        }
    }
}

QDataStream& operator<<(QDataStream &stream, const DataID &dataID)
{
    stream << dataID.id;
    stream << dataID.recurId;
    stream << dataID.title;

    return stream;
}

QDataStream& operator>>(QDataStream &stream, DataID &dataID)
{
    stream >> dataID.id;
    stream >> dataID.recurId;
    stream >> dataID.title;

    return stream;
}

QDataStream& operator<<(QDataStream &stream, const ExtendedData &extData)
{
    stream << extData.id;
    stream << extData.isChecked;
    stream << extData.piningHint;
    stream << extData.countdownHint;

    return stream;
}

QDataStream& operator>>(QDataStream &stream, ExtendedData &extData)
{
    stream >> extData.id;
    stream >> extData.isChecked;
    stream >> extData.piningHint;
    stream >> extData.countdownHint;

    return stream;
}

QDataStream& operator<<(QDataStream &stream, const FloatedData &fdata)
{
    stream << fdata.title;
    stream << fdata.location;

    return stream;
}

QDataStream& operator>>(QDataStream &stream, FloatedData &fdata)
{
    stream >> fdata.title;
    stream >> fdata.location;

    return stream;
}

void DataCenter::loadData(QString filepath)
{
    m_filepath = filepath;
    QFile file(filepath);
    if (file.open(QIODevice::ReadOnly))
    {
        QDataStream stream(&file);
        stream.setVersion(QDataStream::Qt_5_6);
        stream >> m_dataHash;
        stream >> m_floatTasks;
        stream >> mainWindowPos;
    }
}


ExtendedData DataCenter::getExtendedData(const ScheduleDataInfo &schedule)
{
    const DataID dataID(schedule.getID(), schedule.getRecurID(), schedule.getTitleName());
    return m_dataHash[dataID];
}

void DataCenter::setExtendedData(const ScheduleDataInfo &schedule, const ExtendedData &extData)
{
    const DataID dataID(schedule.getID(), schedule.getRecurID(), schedule.getTitleName());
    m_dataHash[dataID] = extData;
    emit extendedDataChanged();
}

void DataCenter::saveData()
{
    QMutableHashIterator<DataID, ExtendedData> i(m_dataHash);
    while (i.hasNext())
    {
        i.next();
        ExtendedData &extData = i.value();
        if (!extData.isChecked && !extData.countdownHint && !extData.piningHint)
            i.remove();
    }

    QFile file(m_filepath);
    if (file.open(QIODevice::WriteOnly))
    {
        QDataStream stream(&file);
        stream.setVersion(QDataStream::Qt_5_6);
        stream << m_dataHash;
        stream << m_floatTasks;
        stream << mainWindowPos;
    }
}

void DataCenter::raiseCalendarWindow()
{
    CalendarDbusUI::instance().RaiseWindow();
}

void DataCenter::OpenSchedule(const QString &strjson)
{
    CalendarDbusUI::instance().OpenSchedule(strjson);
}

bool DataCenter::createFloatTask(QString title)
{
    for (const auto &item : m_floatTasks)
    {
        if (item.title == title)
            return false;
    }
    FloatedData fdata;
    fdata.title = title;
    m_floatTasks.append(fdata);
    return true;
}

bool DataCenter::removeFloatTask(QString title)
{
    for (int i = 0; i < m_floatTasks.count(); i++)
    {
        if (m_floatTasks[i].title == title)
        {
            m_floatTasks.removeAt(i);
            return true;
        }
    }
    return false;
}

void DataCenter::getFloatTask(QVector<FloatedData> &floatTasks)
{
    floatTasks = m_floatTasks;
}

void DataCenter::setFloatTask(QString title, QPoint location)
{
    for (int i = 0; i < m_floatTasks.count(); i++)
    {
        if (m_floatTasks[i].title == title)
        {
            m_floatTasks[i].location = location;
            return;
        }
    }
    FloatedData fdata;
    fdata.title = title;
    fdata.location = location;
    m_floatTasks.append(fdata);
}

bool DataCenter::getJobs(const QDate &startDate, const QDate &endDate, QMap<QDate, QVector<ScheduleDataInfo> > &info)
{
    return CScheduleDBus::getInstance()->GetJobs(startDate, endDate, info);
}

DataCenter::DataCenter()
{
    connect(CScheduleDBus::getInstance(), &CScheduleDBus::jobsUpdate, this, &DataCenter::jobsUpdate);

    QDBusConnection::sessionBus().connect("com.deepin.daemon.Timedate",
                                          "/com/deepin/daemon/Timedate",
                                          "com.deepin.daemon.Timedate",
                                          "TimeUpdate",
                                          this, SIGNAL(timeUpdate()));

    nextDayTimer = new QTimer(this);
    nextDayTimer->setSingleShot(true);
    connect(nextDayTimer, &QTimer::timeout, this, &DataCenter::timeUpdate);
    connect(nextDayTimer, &QTimer::timeout, this, &DataCenter::onNextDay);
    onNextDay();
}

void DataCenter::onNextDay()
{
    QDateTime nextDay(QDate::currentDate().addDays(1), QTime());
    nextDayTimer->setInterval(int(QDateTime::currentDateTime().msecsTo(nextDay) + 1000));
    nextDayTimer->start();
}

